package cn.iocoder.yudao.module.liveprice.service;

import cn.iocoder.yudao.module.liveprice.api.dto.LivePriceRespDTO;
import cn.iocoder.yudao.module.liveprice.apihttp.NowApiTypeEnum;
import cn.iocoder.yudao.module.liveprice.iotdb.IotDbSqlUtil;
import cn.iocoder.yudao.module.liveprice.iotdb.dto.IotDbInsertDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.pool.SessionDataSetWrapper;
import org.apache.iotdb.session.pool.SessionPool;
import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
import org.apache.iotdb.tsfile.read.common.RowRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

import static cn.iocoder.yudao.module.liveprice.iotdb.IotDBConstants.PATH_CONF_PRICE;
import static cn.iocoder.yudao.module.liveprice.iotdb.IotDBConstants.PATH_REAL_PRICE;
import static org.apache.iotdb.tsfile.file.metadata.enums.TSDataType.DOUBLE;

@Service
@Slf4j
public class IotDbApiServiceImpl implements IotDbApiService {
    String[] materialNames = {"gold", "gold_bar", "silver", "platinum", "palladium"};

    @Autowired
    private SessionPool sessionPool;

    @Override
    public void init() {
        try {
            sessionPool.setStorageGroup("root.price.real");
            sessionPool.setStorageGroup("root.price.conf");
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            throw new RuntimeException(e);
        }
        String[] tags = {"price", "reclaim_price", "seal_price", "high_price", "low_price", "open_price", "yesy_price", "change_price", "change_margin"};
        for (NowApiTypeEnum typeEnum : NowApiTypeEnum.values()) {
            for (String tag : tags) {
                String path1 = PATH_REAL_PRICE + "." + typeEnum.getTag() + "." + tag;
                try {
                    if (!sessionPool.checkTimeseriesExists(path1)) {
                        sessionPool.createTimeseries(path1, DOUBLE, TSEncoding.PLAIN, CompressionType.SNAPPY);
                    }
                } catch (IoTDBConnectionException | StatementExecutionException e) {
                    throw new RuntimeException(e);
                }
                String path2 = PATH_CONF_PRICE + "." + typeEnum.getTag() + "." + tag;
                try {
                    if (!sessionPool.checkTimeseriesExists(path2)) {
                        sessionPool.createTimeseries(path2, DOUBLE, TSEncoding.PLAIN, CompressionType.SNAPPY);
                    }
                } catch (IoTDBConnectionException | StatementExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        for (String tag : tags) {
            String path1 = PATH_CONF_PRICE + "." + "gold_bar" + "." + tag;
            try {
                if (!sessionPool.checkTimeseriesExists(path1)) {
                    sessionPool.createTimeseries(path1, DOUBLE, TSEncoding.PLAIN, CompressionType.SNAPPY);
                }
            } catch (IoTDBConnectionException | StatementExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public Boolean insert(String path, IotDbInsertDTO dto) {
        List<String> measurements = new ArrayList<>();
        List<String> values = new ArrayList<>();
        List<IotDbInsertDTO.Item> items = dto.getData();
        long time = dto.getTimestamp();
        for (IotDbInsertDTO.Item item : items) {
            measurements.add(item.getTag());
            values.add(item.getValue());
        }
        try {
            sessionPool.insertRecord(path, time, measurements, values);
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    @Override
    public List<LivePriceRespDTO> queryPriceLast() {
        Map<String, LivePriceRespDTO> map = new HashMap<>();
        // 获取时价
        getPriceRealLast(IotDbSqlUtil.queryRealPriceLast(), map, true);
        // 获取配置价 todo 配置价实时计算？不存入数据库（涉及到 模块与金料种类，数据模式与真实价格不同）
//        getPriceRealLast(IotDbSqlUtil.queryConfPriceLast(), map, false);
        return new ArrayList<>(map.values());
    }

    @Override
    public LivePriceRespDTO queryPriceLast(String tag) {
        Map<String, LivePriceRespDTO> map = new HashMap<>();
        getPriceRealLast(IotDbSqlUtil.queryRealPriceLast(tag), map, true);
        return new ArrayList<>(map.values()).get(0);
    }

    private void getPriceRealLast(String sql, Map<String, LivePriceRespDTO> map, Boolean isReal) {
        SessionDataSetWrapper resultWrapper = null;
        try {
            resultWrapper = sessionPool.executeQueryStatement(sql);
            RowRecord row;
            while ((row = resultWrapper.next()) != null) {
                getPriceLast(row, map, isReal);
            }
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            log.info("RuntimeException：" + e);
            throw new RuntimeException(e);
        } finally {
            sessionPool.closeResultSet(resultWrapper);
        }
    }


    private void getPriceLast(RowRecord row, Map<String, LivePriceRespDTO> map, Boolean isReal) {
        long time = row.getTimestamp();
        String path = row.getFields().get(0).toString();
        String[] tags = path.split("\\.");
        String materialName = tags[tags.length - 2];
        LivePriceRespDTO material = map.getOrDefault(materialName, new LivePriceRespDTO());
        String priceType = tags[tags.length - 1];
        String value = row.getFields().get(1).getStringValue();

        if (Objects.equals(priceType, "reclaim_price")) {
            material.setReclaimPrice(new BigDecimal(value));
        } else if (Objects.equals(priceType, "seal_price")) {
            material.setSalePrice(new BigDecimal(value));
        }

        material.setDateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()));
        map.put(materialName, material);
    }
}
